<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>The source code</title>
  <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
  <script type="text/javascript" src="../prettify/prettify.js"></script>
  <style type="text/css">
    .highlight { display: block; background-color: #ddd; }
  </style>
  <script type="text/javascript">
    function highlight() {
      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
    }
  </script>
</head>
<body onload="prettyPrint(); highlight();">
  <pre class="prettyprint lang-js"><span id='Ext-toolbar-Paging'>/**
</span> * @class Ext.toolbar.Paging
 * @extends Ext.toolbar.Toolbar
 * &lt;p&gt;As the amount of records increases, the time required for the browser to render
 * them increases. Paging is used to reduce the amount of data exchanged with the client.
 * Note: if there are more records/rows than can be viewed in the available screen area, vertical
 * scrollbars will be added.&lt;/p&gt;
 * &lt;p&gt;Paging is typically handled on the server side (see exception below). The client sends
 * parameters to the server side, which the server needs to interpret and then respond with the
 * appropriate data.&lt;/p&gt;
 * &lt;p&gt;&lt;b&gt;Ext.toolbar.Paging&lt;/b&gt; is a specialized toolbar that is bound to a {@link Ext.data.Store}
 * and provides automatic paging control. This Component {@link Ext.data.Store#load load}s blocks
 * of data into the &lt;tt&gt;{@link #store}&lt;/tt&gt; by passing {@link Ext.data.Store#paramNames paramNames} used for
 * paging criteria.&lt;/p&gt;
 *
 * {@img Ext.toolbar.Paging/Ext.toolbar.Paging.png Ext.toolbar.Paging component}
 *
 * &lt;p&gt;PagingToolbar is typically used as one of the Grid's toolbars:&lt;/p&gt;
 * &lt;pre&gt;&lt;code&gt;
 *    var itemsPerPage = 2;   // set the number of items you want per page
 *    
 *    var store = Ext.create('Ext.data.Store', {
 *        id:'simpsonsStore',
 *        autoLoad: false,
 *        fields:['name', 'email', 'phone'],
 *        pageSize: itemsPerPage, // items per page
 *        proxy: {
 *            type: 'ajax',
 *            url: 'pagingstore.js',  // url that will load data with respect to start and limit params
 *            reader: {
 *                type: 'json',
 *                root: 'items',
 *                totalProperty: 'total'
 *            }
 *        }
 *    });
 *    
 *    // specify segment of data you want to load using params
 *    store.load({
 *        params:{
 *            start:0,    
 *            limit: itemsPerPage
 *        }
 *    });
 *    
 *    Ext.create('Ext.grid.Panel', {
 *        title: 'Simpsons',
 *        store: store,
 *        columns: [
 *            {header: 'Name',  dataIndex: 'name'},
 *            {header: 'Email', dataIndex: 'email', flex:1},
 *            {header: 'Phone', dataIndex: 'phone'}
 *        ],
 *        width: 400,
 *        height: 125,
 *        dockedItems: [{
 *            xtype: 'pagingtoolbar',
 *            store: store,   // same store GridPanel is using
 *            dock: 'bottom',
 *            displayInfo: true
 *        }],
 *        renderTo: Ext.getBody()
 *    });
 * &lt;/code&gt;&lt;/pre&gt;
 *
 * &lt;p&gt;To use paging, pass the paging requirements to the server when the store is first loaded.&lt;/p&gt;
 * &lt;pre&gt;&lt;code&gt;
store.load({
    params: {
        // specify params for the first page load if using paging
        start: 0,          
        limit: myPageSize,
        // other params
        foo:   'bar'
    }
});
 * &lt;/code&gt;&lt;/pre&gt;
 * 
 * &lt;p&gt;If using {@link Ext.data.Store#autoLoad store's autoLoad} configuration:&lt;/p&gt;
 * &lt;pre&gt;&lt;code&gt;
var myStore = new Ext.data.Store({
    {@link Ext.data.Store#autoLoad autoLoad}: {start: 0, limit: 25},
    ...
});
 * &lt;/code&gt;&lt;/pre&gt;
 * 
 * &lt;p&gt;The packet sent back from the server would have this form:&lt;/p&gt;
 * &lt;pre&gt;&lt;code&gt;
{
    &quot;success&quot;: true,
    &quot;results&quot;: 2000, 
    &quot;rows&quot;: [ // &lt;b&gt;*Note:&lt;/b&gt; this must be an Array 
        { &quot;id&quot;:  1, &quot;name&quot;: &quot;Bill&quot;, &quot;occupation&quot;: &quot;Gardener&quot; },
        { &quot;id&quot;:  2, &quot;name&quot;:  &quot;Ben&quot;, &quot;occupation&quot;: &quot;Horticulturalist&quot; },
        ...
        { &quot;id&quot;: 25, &quot;name&quot;:  &quot;Sue&quot;, &quot;occupation&quot;: &quot;Botanist&quot; }
    ]
}
 * &lt;/code&gt;&lt;/pre&gt;
 * &lt;p&gt;&lt;u&gt;Paging with Local Data&lt;/u&gt;&lt;/p&gt;
 * &lt;p&gt;Paging can also be accomplished with local data using extensions:&lt;/p&gt;
 * &lt;div class=&quot;mdetail-params&quot;&gt;&lt;ul&gt;
 * &lt;li&gt;&lt;a href=&quot;http://sencha.com/forum/showthread.php?t=71532&quot;&gt;Ext.ux.data.PagingStore&lt;/a&gt;&lt;/li&gt;
 * &lt;li&gt;Paging Memory Proxy (examples/ux/PagingMemoryProxy.js)&lt;/li&gt;
 * &lt;/ul&gt;&lt;/div&gt;
 */
Ext.define('Ext.toolbar.Paging', {
    extend: 'Ext.toolbar.Toolbar',
    alias: 'widget.pagingtoolbar',
    alternateClassName: 'Ext.PagingToolbar',
    requires: ['Ext.toolbar.TextItem', 'Ext.form.field.Number'],
<span id='Ext-toolbar-Paging-cfg-store'>    /**
</span>     * @cfg {Ext.data.Store} store
     * The {@link Ext.data.Store} the paging toolbar should use as its data source (required).
     */
<span id='Ext-toolbar-Paging-cfg-displayInfo'>    /**
</span>     * @cfg {Boolean} displayInfo
     * &lt;tt&gt;true&lt;/tt&gt; to display the displayMsg (defaults to &lt;tt&gt;false&lt;/tt&gt;)
     */
    displayInfo: false,
<span id='Ext-toolbar-Paging-cfg-prependButtons'>    /**
</span>     * @cfg {Boolean} prependButtons
     * &lt;tt&gt;true&lt;/tt&gt; to insert any configured &lt;tt&gt;items&lt;/tt&gt; &lt;i&gt;before&lt;/i&gt; the paging buttons.
     * Defaults to &lt;tt&gt;false&lt;/tt&gt;.
     */
    prependButtons: false,
<span id='Ext-toolbar-Paging-cfg-displayMsg'>    /**
</span>     * @cfg {String} displayMsg
     * The paging status message to display (defaults to &lt;tt&gt;'Displaying {0} - {1} of {2}'&lt;/tt&gt;).
     * Note that this string is formatted using the braced numbers &lt;tt&gt;{0}-{2}&lt;/tt&gt; as tokens
     * that are replaced by the values for start, end and total respectively. These tokens should
     * be preserved when overriding this string if showing those values is desired.
     */
    displayMsg : 'Displaying {0} - {1} of {2}',
<span id='Ext-toolbar-Paging-cfg-emptyMsg'>    /**
</span>     * @cfg {String} emptyMsg
     * The message to display when no records are found (defaults to 'No data to display')
     */
    emptyMsg : 'No data to display',
<span id='Ext-toolbar-Paging-cfg-beforePageText'>    /**
</span>     * @cfg {String} beforePageText
     * The text displayed before the input item (defaults to &lt;tt&gt;'Page'&lt;/tt&gt;).
     */
    beforePageText : 'Page',
<span id='Ext-toolbar-Paging-cfg-afterPageText'>    /**
</span>     * @cfg {String} afterPageText
     * Customizable piece of the default paging text (defaults to &lt;tt&gt;'of {0}'&lt;/tt&gt;). Note that
     * this string is formatted using &lt;tt&gt;{0}&lt;/tt&gt; as a token that is replaced by the number of
     * total pages. This token should be preserved when overriding this string if showing the
     * total page count is desired.
     */
    afterPageText : 'of {0}',
<span id='Ext-toolbar-Paging-cfg-firstText'>    /**
</span>     * @cfg {String} firstText
     * The quicktip text displayed for the first page button (defaults to &lt;tt&gt;'First Page'&lt;/tt&gt;).
     * &lt;b&gt;Note&lt;/b&gt;: quick tips must be initialized for the quicktip to show.
     */
    firstText : 'First Page',
<span id='Ext-toolbar-Paging-cfg-prevText'>    /**
</span>     * @cfg {String} prevText
     * The quicktip text displayed for the previous page button (defaults to &lt;tt&gt;'Previous Page'&lt;/tt&gt;).
     * &lt;b&gt;Note&lt;/b&gt;: quick tips must be initialized for the quicktip to show.
     */
    prevText : 'Previous Page',
<span id='Ext-toolbar-Paging-cfg-nextText'>    /**
</span>     * @cfg {String} nextText
     * The quicktip text displayed for the next page button (defaults to &lt;tt&gt;'Next Page'&lt;/tt&gt;).
     * &lt;b&gt;Note&lt;/b&gt;: quick tips must be initialized for the quicktip to show.
     */
    nextText : 'Next Page',
<span id='Ext-toolbar-Paging-cfg-lastText'>    /**
</span>     * @cfg {String} lastText
     * The quicktip text displayed for the last page button (defaults to &lt;tt&gt;'Last Page'&lt;/tt&gt;).
     * &lt;b&gt;Note&lt;/b&gt;: quick tips must be initialized for the quicktip to show.
     */
    lastText : 'Last Page',
<span id='Ext-toolbar-Paging-cfg-refreshText'>    /**
</span>     * @cfg {String} refreshText
     * The quicktip text displayed for the Refresh button (defaults to &lt;tt&gt;'Refresh'&lt;/tt&gt;).
     * &lt;b&gt;Note&lt;/b&gt;: quick tips must be initialized for the quicktip to show.
     */
    refreshText : 'Refresh',
<span id='Ext-toolbar-Paging-cfg-inputItemWidth'>    /**
</span>     * @cfg {Number} inputItemWidth
     * The width in pixels of the input field used to display and change the current page number (defaults to 30).
     */
    inputItemWidth : 30,
    
<span id='Ext-toolbar-Paging-method-getPagingItems'>    /**
</span>     * Gets the standard paging items in the toolbar
     * @private
     */
    getPagingItems: function() {
        var me = this;
        
        return [{
            itemId: 'first',
            tooltip: me.firstText,
            overflowText: me.firstText,
            iconCls: Ext.baseCSSPrefix + 'tbar-page-first',
            disabled: true,
            handler: me.moveFirst,
            scope: me
        },{
            itemId: 'prev',
            tooltip: me.prevText,
            overflowText: me.prevText,
            iconCls: Ext.baseCSSPrefix + 'tbar-page-prev',
            disabled: true,
            handler: me.movePrevious,
            scope: me
        },
        '-',
        me.beforePageText,
        {
            xtype: 'numberfield',
            itemId: 'inputItem',
            name: 'inputItem',
            cls: Ext.baseCSSPrefix + 'tbar-page-number',
            allowDecimals: false,
            minValue: 1,
            hideTrigger: true,
            enableKeyEvents: true,
            selectOnFocus: true,
            submitValue: false,
            width: me.inputItemWidth,
            margins: '-1 2 3 2',
            listeners: {
                scope: me,
                keydown: me.onPagingKeyDown,
                blur: me.onPagingBlur
            }
        },{
            xtype: 'tbtext',
            itemId: 'afterTextItem',
            text: Ext.String.format(me.afterPageText, 1)
        },
        '-',
        {
            itemId: 'next',
            tooltip: me.nextText,
            overflowText: me.nextText,
            iconCls: Ext.baseCSSPrefix + 'tbar-page-next',
            disabled: true,
            handler: me.moveNext,
            scope: me
        },{
            itemId: 'last',
            tooltip: me.lastText,
            overflowText: me.lastText,
            iconCls: Ext.baseCSSPrefix + 'tbar-page-last',
            disabled: true,
            handler: me.moveLast,
            scope: me
        },
        '-',
        {
            itemId: 'refresh',
            tooltip: me.refreshText,
            overflowText: me.refreshText,
            iconCls: Ext.baseCSSPrefix + 'tbar-loading',
            handler: me.doRefresh,
            scope: me
        }];
    },

    initComponent : function(){
        var me = this,
            pagingItems = me.getPagingItems(),
            userItems   = me.items || me.buttons || [];
            
        if (me.prependButtons) {
            me.items = userItems.concat(pagingItems);
        } else {
            me.items = pagingItems.concat(userItems);
        }
        delete me.buttons;
        
        if (me.displayInfo) {
            me.items.push('-&gt;');
            me.items.push({xtype: 'tbtext', itemId: 'displayItem'});
        }
        
        me.callParent();
        
        me.addEvents(
<span id='Ext-toolbar-Paging-event-change'>            /**
</span>             * @event change
             * Fires after the active page has been changed.
             * @param {Ext.toolbar.Paging} this
             * @param {Object} pageData An object that has these properties:&lt;ul&gt;
             * &lt;li&gt;&lt;code&gt;total&lt;/code&gt; : Number &lt;div class=&quot;sub-desc&quot;&gt;The total number of records in the dataset as
             * returned by the server&lt;/div&gt;&lt;/li&gt;
             * &lt;li&gt;&lt;code&gt;currentPage&lt;/code&gt; : Number &lt;div class=&quot;sub-desc&quot;&gt;The current page number&lt;/div&gt;&lt;/li&gt;
             * &lt;li&gt;&lt;code&gt;pageCount&lt;/code&gt; : Number &lt;div class=&quot;sub-desc&quot;&gt;The total number of pages (calculated from
             * the total number of records in the dataset as returned by the server and the current {@link #pageSize})&lt;/div&gt;&lt;/li&gt;
             * &lt;li&gt;&lt;code&gt;toRecord&lt;/code&gt; : Number &lt;div class=&quot;sub-desc&quot;&gt;The starting record index for the current page&lt;/div&gt;&lt;/li&gt;
             * &lt;li&gt;&lt;code&gt;fromRecord&lt;/code&gt; : Number &lt;div class=&quot;sub-desc&quot;&gt;The ending record index for the current page&lt;/div&gt;&lt;/li&gt;
             * &lt;/ul&gt;
             */
            'change',
<span id='Ext-toolbar-Paging-event-beforechange'>            /**
</span>             * @event beforechange
             * Fires just before the active page is changed.
             * Return false to prevent the active page from being changed.
             * @param {Ext.toolbar.Paging} this
             * @param {Number} page The page number that will be loaded on change 
             */
            'beforechange'
        );
        me.on('afterlayout', me.onLoad, me, {single: true});

        me.bindStore(me.store, true);
    },
    // private
    updateInfo : function(){
        var me = this,
            displayItem = me.child('#displayItem'),
            store = me.store,
            pageData = me.getPageData(),
            count, msg;

        if (displayItem) {
            count = store.getCount();
            if (count === 0) {
                msg = me.emptyMsg;
            } else {
                msg = Ext.String.format(
                    me.displayMsg,
                    pageData.fromRecord,
                    pageData.toRecord,
                    pageData.total
                );
            }
            displayItem.setText(msg);
            me.doComponentLayout();
        }
    },

    // private
    onLoad : function(){
        var me = this,
            pageData,
            currPage,
            pageCount,
            afterText;
            
        if (!me.rendered) {
            return;
        }

        pageData = me.getPageData();
        currPage = pageData.currentPage;
        pageCount = pageData.pageCount;
        afterText = Ext.String.format(me.afterPageText, isNaN(pageCount) ? 1 : pageCount);

        me.child('#afterTextItem').setText(afterText);
        me.child('#inputItem').setValue(currPage);
        me.child('#first').setDisabled(currPage === 1);
        me.child('#prev').setDisabled(currPage === 1);
        me.child('#next').setDisabled(currPage === pageCount);
        me.child('#last').setDisabled(currPage === pageCount);
        me.child('#refresh').enable();
        me.updateInfo();
        me.fireEvent('change', me, pageData);
    },

    // private
    getPageData : function(){
        var store = this.store,
            totalCount = store.getTotalCount();
            
        return {
            total : totalCount,
            currentPage : store.currentPage,
            pageCount: Math.ceil(totalCount / store.pageSize),
            fromRecord: ((store.currentPage - 1) * store.pageSize) + 1,
            toRecord: Math.min(store.currentPage * store.pageSize, totalCount)
            
        };
    },

    // private
    onLoadError : function(){
        if (!this.rendered) {
            return;
        }
        this.child('#refresh').enable();
    },

    // private
    readPageFromInput : function(pageData){
        var v = this.child('#inputItem').getValue(),
            pageNum = parseInt(v, 10);
            
        if (!v || isNaN(pageNum)) {
            this.child('#inputItem').setValue(pageData.currentPage);
            return false;
        }
        return pageNum;
    },

    onPagingFocus : function(){
        this.child('#inputItem').select();
    },

    //private
    onPagingBlur : function(e){
        var curPage = this.getPageData().currentPage;
        this.child('#inputItem').setValue(curPage);
    },

    // private
    onPagingKeyDown : function(field, e){
        var me = this,
            k = e.getKey(),
            pageData = me.getPageData(),
            increment = e.shiftKey ? 10 : 1,
            pageNum;

        if (k == e.RETURN) {
            e.stopEvent();
            pageNum = me.readPageFromInput(pageData);
            if (pageNum !== false) {
                pageNum = Math.min(Math.max(1, pageNum), pageData.pageCount);
                if(me.fireEvent('beforechange', me, pageNum) !== false){
                    me.store.loadPage(pageNum);
                }
            }
        } else if (k == e.HOME || k == e.END) {
            e.stopEvent();
            pageNum = k == e.HOME ? 1 : pageData.pageCount;
            field.setValue(pageNum);
        } else if (k == e.UP || k == e.PAGEUP || k == e.DOWN || k == e.PAGEDOWN) {
            e.stopEvent();
            pageNum = me.readPageFromInput(pageData);
            if (pageNum) {
                if (k == e.DOWN || k == e.PAGEDOWN) {
                    increment *= -1;
                }
                pageNum += increment;
                if (pageNum &gt;= 1 &amp;&amp; pageNum &lt;= pageData.pages) {
                    field.setValue(pageNum);
                }
            }
        }
    },

    // private
    beforeLoad : function(){
        if(this.rendered &amp;&amp; this.refresh){
            this.refresh.disable();
        }
    },

    // private
    doLoad : function(start){
        if(this.fireEvent('beforechange', this, o) !== false){
            this.store.load();
        }
    },

<span id='Ext-toolbar-Paging-method-moveFirst'>    /**
</span>     * Move to the first page, has the same effect as clicking the 'first' button.
     */
    moveFirst : function(){
        if (this.fireEvent('beforechange', this, 1) !== false){
            this.store.loadPage(1);
        }
    },

<span id='Ext-toolbar-Paging-method-movePrevious'>    /**
</span>     * Move to the previous page, has the same effect as clicking the 'previous' button.
     */
    movePrevious : function(){
        var me = this,
            prev = me.store.currentPage - 1;
        
        if (prev &gt; 0) {
            if (me.fireEvent('beforechange', me, prev) !== false) {
                me.store.previousPage();
            }
        }
    },

<span id='Ext-toolbar-Paging-method-moveNext'>    /**
</span>     * Move to the next page, has the same effect as clicking the 'next' button.
     */
    moveNext : function(){
        var me = this,
            total = me.getPageData().pageCount,
            next = me.store.currentPage + 1;
               
        if (next &lt;= total) {
            if (me.fireEvent('beforechange', me, next) !== false) {
                me.store.nextPage();
            }
        }
    },

<span id='Ext-toolbar-Paging-method-moveLast'>    /**
</span>     * Move to the last page, has the same effect as clicking the 'last' button.
     */
    moveLast : function(){
        var me = this, 
            last = me.getPageData().pageCount;
        
        if (me.fireEvent('beforechange', me, last) !== false) {
            me.store.loadPage(last);
        }
    },

<span id='Ext-toolbar-Paging-method-doRefresh'>    /**
</span>     * Refresh the current page, has the same effect as clicking the 'refresh' button.
     */
    doRefresh : function(){
        var me = this,
            current = me.store.currentPage;
        
        if (me.fireEvent('beforechange', me, current) !== false) {
            me.store.loadPage(current);
        }
    },

<span id='Ext-toolbar-Paging-method-bindStore'>    /**
</span>     * Binds the paging toolbar to the specified {@link Ext.data.Store}
     * @param {Store} store The store to bind to this toolbar
     * @param {Boolean} initial (Optional) true to not remove listeners
     */
    bindStore : function(store, initial){
        var me = this;
        
        if (!initial &amp;&amp; me.store) {
            if(store !== me.store &amp;&amp; me.store.autoDestroy){
                me.store.destroy();
            }else{
                me.store.un('beforeload', me.beforeLoad, me);
                me.store.un('load', me.onLoad, me);
                me.store.un('exception', me.onLoadError, me);
            }
            if(!store){
                me.store = null;
            }
        }
        if (store) {
            store = Ext.data.StoreManager.lookup(store);
            store.on({
                scope: me,
                beforeload: me.beforeLoad,
                load: me.onLoad,
                exception: me.onLoadError
            });
        }
        me.store = store;
    },

<span id='Ext-toolbar-Paging-method-unbind'>    /**
</span>     * Unbinds the paging toolbar from the specified {@link Ext.data.Store} &lt;b&gt;(deprecated)&lt;/b&gt;
     * @param {Ext.data.Store} store The data store to unbind
     */
    unbind : function(store){
        this.bindStore(null);
    },

<span id='Ext-toolbar-Paging-method-bind'>    /**
</span>     * Binds the paging toolbar to the specified {@link Ext.data.Store} &lt;b&gt;(deprecated)&lt;/b&gt;
     * @param {Ext.data.Store} store The data store to bind
     */
    bind : function(store){
        this.bindStore(store);
    },

    // private
    onDestroy : function(){
        this.bindStore(null);
        this.callParent();
    }
});
</pre>
</body>
</html>
